package com.ludomancy.starwave;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Iterator;
import java.util.Vector;

import com.ludomancy.gamelib.GameCanvas;
import com.ludomancy.gamelib.ResourceManager;
import com.ludomancy.gamelib.Sound;
import com.ludomancy.gamelib.Utils;
import com.ludomancy.gamelib.Widget;

public class GridWidget extends Widget implements ShipTypes
{

	public Vector pendingAnimations = new Vector();
	public Vector shipCorpses = new Vector();
	private Ship hoveringOverShip = null;
	private Vector placedShips = new Vector();
	
	private static Vector editorShips = new Vector();

	public GridWidget(Widget parent, Rectangle area, int[][] playerSetup)
	{
		super(parent, area);
		if(playerSetup == null)
		{
			playerSetup = Game.game.me.initialSetup;
		}
		else
		{
			Game.game.me.initialSetup = playerSetup;
		}
		
		if(Game.EDITOR)
		{
			Game.game.ships = (Vector) editorShips.clone();
		}
		
		for (int i = 0; i < playerSetup.length; i++)
		{
			for (int j = 0; j < playerSetup[i].length; j++)
			{
				int type = playerSetup[i][j]; 
				if( type != NON)
				{
					Ship newShip = Ship.createPrototype(type, Game.game.me, j, i);
					Game.game.addShip(newShip);
				}
			}
		}
	}
	
	public void addCorpse(Ship corpse)
	{
		for (Iterator iter = shipCorpses.iterator(); iter.hasNext();)
		{
			Ship ship = (Ship) iter.next();
			if(ship.type == corpse.type && ship.px == corpse.px && ship.py == corpse.py)
			{
				// do not add
				return;
			}
		}
		shipCorpses.add(corpse);
	}
	
	public void clearCorpses()
	{
		shipCorpses.clear();
	}
	
	public Vector getPlayerShips()
	{
		return placedShips;
	}
	
	private Point getTilePosition(Point cursor)
	{
		return new Point(cursor.x / Settings.TILE_SIZE, cursor.y / Settings.TILE_SIZE);
	}
	
	private Ship getShipUnderCursor(Point cursor)
	{
		Point p = getTilePosition(cursor);
		Ship[] s = Game.game.getShips(p.x, p.y);
		return s.length > 0 ? s[0] : null;
	}
	
	public void setSelectedShip(Ship ship)
	{
		hoveringOverShip = ship;
		StarWaveApplet.mainWidget.infoBar.setSelectedShip(hoveringOverShip);
	}
	
	public Object beginDrag(Point cursor)
	{
		Ship ship = getShipUnderCursor(cursor);
		if(ship != null && ship.owner == Game.game.me)
		{
			setSelectedShip(ship);
			return ship;
		}
		else
		{
			return null;
		}
	}
	
	public void onDrag(Point cursor, Object object)
	{
	}

	public void onDrop(Widget issuer, Point cursor, Object dragObject)
	{
		if(dragObject instanceof Ship)
		{
			Ship ship = (Ship) dragObject;
			putShip(ship, getTilePosition(cursor));
			new Sound("pop.wav").play();
		}
		setSelectedShip(getShipUnderCursor(cursor));
	}
	
	public boolean paintDragCursor(Graphics g, Point pos, Object dropObject, boolean targetAccepted)
	{
		Ship ship = (Ship) dropObject;
		if(ship != null && targetAccepted)
		{
			ship.paintInYard(g, pos.x - Settings.TILE_SIZE/2, pos.y - Settings.TILE_SIZE/2);
			Point relative = screenToWidget(pos);
			if(getAbsoluteBounds().contains(pos))
			{
				Point tile = getTilePosition(relative);
				Utils.pushTranslation();
				Utils.translate(g, getAbsoluteBounds().x, getAbsoluteBounds().y);
				paintAttackRadius(g, ship, tile.x, tile.y);
				// affordance hint (if drop position is valid)
				paintOutline(g, ship, tile.x, tile.y);
				Utils.popTranslation(g);
			}
			return true;
		}
		return false;
	}
	
	public void paintOutline(Graphics g, Ship ship, int tilex, int tiley)
	{
		if(Game.game.isInDeploymentField(new Point(tilex, tiley)))
		{
			g.setColor(Color.GREEN);
			int ox = tilex*Settings.TILE_SIZE;
			int oy = tiley*Settings.TILE_SIZE;
			int lineLen = Settings.TILE_SIZE/4;
			g.drawLine(ox, oy, ox + lineLen, oy);
			g.drawLine(ox, oy, ox, oy + lineLen);

			g.drawLine(ox+Settings.TILE_SIZE, oy+Settings.TILE_SIZE, ox+Settings.TILE_SIZE - lineLen, oy+Settings.TILE_SIZE);
			g.drawLine(ox+Settings.TILE_SIZE, oy+Settings.TILE_SIZE, ox+Settings.TILE_SIZE, oy+Settings.TILE_SIZE - lineLen);
		}		
	}
		
	public void paintAttackRadius(Graphics g, Ship ship, int tilex, int tiley)
	{
		Point[] arc = ship.getFiringArc();
		for (int k = 0; k < arc.length; k++)
		{
			int tx = arc[k].x + tilex;
			int ty = arc[k].y + tiley;
			// only draw inside a valid grid
			if( Game.game.isWithinPlayfield(tx, ty))
			{
				if(tx != tilex || ty != tiley)
				{
					Image icon = ResourceManager.getImage("crosshair.png"); 
					g.drawImage(icon, tx*Settings.TILE_SIZE, ty*Settings.TILE_SIZE, null);
				}
			}
		}
		paintPath(g, ship, tilex, tiley);
	}
	
	private void paintPath(Graphics g, Ship ship, int tilex, int tiley)
	{
		if(ship.isMover())
		{
			Point[] arc = ship.getPath(tilex, tiley);
			for (int k = 0; k < arc.length; k++)
			{
				int tx = arc[k].x;
				int ty = arc[k].y;
				// only draw inside a valid grid
				if( Game.game.isWithinPlayfield(tx, ty) && !ship.canFire(tx, ty))
				{
					if(tx != ship.px || ty != ship.py)
					{
						Image icon = ResourceManager.getImage("path.png"); 
						g.drawImage(icon, tx*Settings.TILE_SIZE, ty*Settings.TILE_SIZE, null);
					}
				}
			}
		}
	}

	public void mouseMoved(Point point)
	{
		Ship sel = getShipUnderCursor(point);
		setSelectedShip(sel);
	}
	
	public void onMouseLeave()
	{
		setSelectedShip(null);
	}
	
	public void putShip(Ship ship, Point tile)
	{
		if(Game.game.isInDeploymentField(tile))
		{
			Ship[] currentShip = Game.game.getShips(tile.x, tile.y);
			if (currentShip.length > 0)
			{
				swapShips(currentShip[0], ship);
				swapInitialSetup(currentShip[0].px, currentShip[0].py, ship.px, ship.py);
			}
			else
			{
				swapInitialSetup(tile.x, tile.y, ship.px, ship.py);
				ship.px = tile.x;
				ship.py = tile.y;
			}
			setSelectedShip(ship);
		}
	}
	
	private void swapInitialSetup(int x1, int y1, int x2, int y2)
	{
		int oldType = Game.game.me.initialSetup[y1][x1];
		Game.game.me.initialSetup[y1][x1] = Game.game.me.initialSetup[y2][x2];
		Game.game.me.initialSetup[y2][x2] = oldType; 		
	}
	
	public void swapShips(Ship ship1, Ship ship2)
	{
		// swap initial setup
		int tempX = ship1.px;
		int tempY = ship1.py;
		ship1.px = ship2.px;
		ship1.py = ship2.py;
		ship2.px = tempX;
		ship2.py = tempY;
	}
			
	public void mouseUp(Point pos)
	{
	}
	
	public void mouseDown(Point pos)
	{
		if(Game.game.phase != Game.PHASE_SETUP)
		{
			return;
		}
		if(Game.EDITOR && StarWaveWidget.shiftPressed)
		{
			Point tile = getTilePosition(pos);
			Ship[] old = Game.game.getShips(tile.x, tile.y);
			int curType = old.length > 0 ? old[0].type : NON;
			if(old.length > 0)
			{
				Game.game.ships.remove(old[0]);
				for (Iterator iter = editorShips.iterator(); iter.hasNext();)
				{
					Ship ship = (Ship) iter.next();
					if(ship.px == tile.x && ship.py == tile.y)
					{
						editorShips.remove(ship);
						break;
					}
				}
				editorShips.remove(old[0]);
			}
			int newType = getNextType(curType, tile.x);
			if(newType != NON)
			{
				Ship newShip = Ship.createPrototype(newType, tile.x > Game.ACTIVE_AREA_LENGTH ? Game.game.ai : Game.game.me,tile.x, tile.y);
				editorShips.add(Ship.createPrototype(newType, tile.x > Game.ACTIVE_AREA_LENGTH ? Game.game.ai : Game.game.me,tile.x, tile.y));
				Game.game.addShip(newShip);
			}
			return;
		}
		GameCanvas.screen.forceDragStart(this, widgetToScreen(pos));
	}
		
	private int getNextType(int oldType, int x)
	{
		switch(oldType)
		{
			case NON:
				return x > Game.ACTIVE_AREA_LENGTH ? FIG : MIN;
			case MIN:
				return DEF;
			case DEF:
				return POR;
			case POR:
				return NON;
			case FIG:
				return FIA;
			case FIA:
				return SLI;
			case SLI:
				return SLU;
			default:
				return NON;
		}
	}
		
	public void paint(Graphics g)
	{
		Graphics2D g2 = (Graphics2D) g;
		int gridWidth = Game.GRID_WIDTH;
		int gridHeight = Game.GRID_HEIGHT;
		
		if(Game.game.phase == Game.PHASE_SETUP)
		{
			// inactive area
			Utils.pushAlpha(g, 0.50f);
			g2.setColor(Color.BLACK);
			g2.fillRect(Settings.TILE_SIZE*Game.ACTIVE_AREA_LENGTH, 0, Settings.TILE_SIZE*(Game.GRID_WIDTH-Game.ACTIVE_AREA_LENGTH), getHeight()-1);
//			g2.setColor(Color.WHITE);
//			g2.fillRect(0, 0, Settings.TILE_SIZE*Game.ACTIVE_AREA_LENGTH, getHeight()-1);
			Utils.popAlpha(g);
			// grid
			g2.setColor(Settings.GRID_LINES_COLOR);
			for (int y=0; y!= gridHeight+1; y++)
			{
				g2.drawLine(0, y*Settings.TILE_SIZE, gridWidth*Settings.TILE_SIZE, y*Settings.TILE_SIZE);
				for (int x=0; x!= gridWidth+1; x++)
				{
					g2.drawLine(x*Settings.TILE_SIZE, 0, x*Settings.TILE_SIZE, gridHeight*Settings.TILE_SIZE);
				}
			}
			// active area
			g2.setColor(Color.WHITE);
			g2.drawRect(0, 0, Settings.TILE_SIZE*Game.ACTIVE_AREA_LENGTH, getHeight()-1);
		}
		else
		{
			g2.setColor(Settings.GRID_LINES_COLOR);
			g2.drawRect(0, 0, getWidth()-1, getHeight()-1);
		}
		
		// paint ship corpses
		for (int i = 0; i < shipCorpses.size(); i++)
		{
			Ship corpse = (Ship) shipCorpses.elementAt(i);
			corpse.paintDead(g, corpse.px*Settings.TILE_SIZE, corpse.py*Settings.TILE_SIZE);
		}
		
		// here we process the queued animations
		//if(Game.game.phase == Game.PHASE_ATTACKING || Game.game.phase == Game.PHASE_MOVING)
		if(!pendingAnimations.isEmpty())
		{
			boolean finishedAnimations = true;
			Animation singleAnimation = null;
			for (int i = 0; i < pendingAnimations.size(); i++)
			{
				Animation anim = (Animation) pendingAnimations.elementAt(i);
				if(!anim.isOver())
				{
					// attack animations are executed one after the other
					if(anim instanceof AttackAnimation && singleAnimation == null)
					{
						singleAnimation = anim;
						singleAnimation.start();
						finishedAnimations = false;
						continue;
					}
				}
				anim.update();
				anim.paint(g2);
				if(!anim.isOver())
				{
					finishedAnimations = false;
				}
			}
			// single animations are executed at the end so they are not obscured
			if(singleAnimation != null)
			{
				singleAnimation.update();
				singleAnimation.paint(g2);
			}
	
			if(finishedAnimations)
			{
				pendingAnimations.clear();
				Game.game.endPhase();
			}
			
		}
		else
		{
			// ships
			for (int i = 0; i < Game.game.ships.size(); i++)
			{
				Ship ship = (Ship) Game.game.ships.elementAt(i);
				// offset effect for increasing ship affordance
				if(ship == hoveringOverShip && ship.owner == Game.game.me)
				{
					if(ship == getDragObject())
					{
						Utils.pushAlpha(g, 0.5f);
						ship.paint(g, ship.px*Settings.TILE_SIZE-1, ship.py*Settings.TILE_SIZE-1);
						Utils.popAlpha(g);
					}
					else
					{
						ship.paint(g, ship.px*Settings.TILE_SIZE-1, ship.py*Settings.TILE_SIZE-1);
					}
				}
				else
				{
					ship.paint(g, ship.px*Settings.TILE_SIZE, ship.py*Settings.TILE_SIZE);
				}
			}
			// paint attack radius
			if(hoveringOverShip != null && getDragObject() == null)
			{
				paintAttackRadius(g, hoveringOverShip, hoveringOverShip.px, hoveringOverShip.py);
			}
		}
	}
}
